package server.util;

import javax.servlet.http.Cookie;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;

public final class RequestUtil {
    //从contentType中解析出字符编码
    public static String parseCharacterEncoding(String contentType) {
        if (contentType == null)
            return null;
        int start = contentType.indexOf("charset=");
        if (start < 0)
            return null;
        String encoding = contentType.substring(start + 8);
        int end = encoding.indexOf(';');
        if (end >= 0) {
            encoding = encoding.substring(0, end);
        }
        encoding = encoding.trim();
        //如将带引号的"utf-8"转换为不带引号的utf-8
        if (encoding.length() > 2 && encoding.startsWith("\"") && encoding.endsWith("\"")) {
            encoding = encoding.substring(1, encoding.length() - 1);
        }
        return encoding.trim();
    }
    //修正uri 路径正常或可修正返回修正后的内容，否则返回null
    public static String normalizeUri(String path) {
        if (path == null)
            return null;
        String normalized = path;
        // %7E或%7e代表~ 修正
        if (normalized.startsWith("/%7E") || normalized.startsWith("/%7e"))
            normalized = "/~" + normalized.substring(4);
        // % / . \是特殊字符，不能以%xx形式存在于uri中
        if ((normalized.indexOf("%25") >= 0)
                || (normalized.indexOf("%2F") >= 0)
                || (normalized.indexOf("%2E") >= 0)
                || (normalized.indexOf("%5C") >= 0)
                || (normalized.indexOf("%2f") >= 0)
                || (normalized.indexOf("%2e") >= 0)
                || (normalized.indexOf("%5c") >= 0)) {
            return null;
        }
        // /.修正为/
        if (normalized.equals("/."))
            return "/";

        // \修正为/
        if (normalized.indexOf('\\') >= 0)
            normalized = normalized.replace('\\', '/');
        // 如果uri不以/开头，加上/
        if (!normalized.startsWith("/"))
            normalized = "/" + normalized;

        // 把//改为/
        while (true) {
            int index = normalized.indexOf("//");
            if (index < 0)
                break;
            normalized = normalized.substring(0, index) +
                    normalized.substring(index + 1);
        }

        // 把/./改为/
        while (true) {
            int index = normalized.indexOf("/./");
            if (index < 0)
                break;
            normalized = normalized.substring(0, index) +
                    normalized.substring(index + 2);
        }

        // 把/../这种相对路径改为实际路径
        while (true) {
            int index = normalized.indexOf("/../");
            if (index < 0)
                break;
            //越出根目录边界
            if (index == 0)
                return (null);
            int index2 = normalized.lastIndexOf('/', index - 1);
            normalized = normalized.substring(0, index2) +
                    normalized.substring(index + 3);
        }

        // 不能存在/...
        if (normalized.indexOf("/...") >= 0)
            return (null);
        return normalized;
    }

    //将请求头的value解析为Cookie数组
    public static Cookie[] parseCookieHeader(String header) {

        if ((header == null) || (header.length() < 1))
            return new Cookie[0];

        ArrayList<Cookie> cookies = new ArrayList<>();
        while (header.length() > 0) {
            int semicolonIndex = header.indexOf(';');
            if (semicolonIndex < 0)
                semicolonIndex = header.length();
            if (semicolonIndex == 0)
                break;
            String token = header.substring(0, semicolonIndex);
            if (semicolonIndex < header.length())
                header = header.substring(semicolonIndex + 1);
            else
                header = "";
            try {
                int equalsIndex = token.indexOf('=');
                if (equalsIndex > 0) {
                    String name = token.substring(0, equalsIndex).trim();
                    String value = token.substring(equalsIndex + 1).trim();
                    cookies.add(new Cookie(name, value));
                }
            } catch (Throwable e) {
                ;
            }
        }
        return cookies.toArray(new Cookie[cookies.size()]);
    }
    //将请求头的Accept-Language解析为Locale数组
    public static Locale[] parseAcceptLanguage(String value) {
        TreeMap<Double, ArrayList<Locale>> locales = new TreeMap<>();
        //删除空格和制表符
        StringBuffer sb = new StringBuffer(value.length());
        for (int i = 0; i < value.length(); i++) {
            char c = value.charAt(i);
            if (c != ' ' && c != '\t') {
                sb.append(c);
            }
        }
        value = sb.toString();
        int count = 0;
        while (true) {
            int commaIndex = value.indexOf(',');
            if (commaIndex <= 0)
                break;
            //要解析的一个实体
            String entry = value.substring(commaIndex);
            value = value.substring(commaIndex + 1);

            //未声明权重ze为1
            double quality = 1.0;
            int i = entry.indexOf(";q=");
            if(i >= 0) {
                try {
                    Double.parseDouble(entry.substring(i+3));
                } catch (Exception e) {
                    //解析失败,权重设为0
                    quality = 0.0;
                }
                entry = entry.substring(0, i);
            }
            //权重太小,直接忽略
            if (quality < 0.00005)
                continue;
            //*代表所有语言都支持,直接忽略
            if ("*".equals(entry))
                continue;

            //解析出语言、国家、地区
            String language = null;
            String country = null;
            String variant = null;
            i = entry.indexOf('-');
            if (i < 0) {
                language = entry;
                country = "";
                variant = "";
            } else {
                language = entry.substring(0, i);
                country = entry.substring(i + 1);
                i = country.indexOf('-');
                if (i < 0) {
                    variant = "";
                } else {
                    variant = country.substring(i + 1);
                    country = country.substring(0, i);
                }
            }
            Locale locale = new Locale(language, country, variant);
            //TreeMap升序排序,加-把权值最大的排第一
            Double key = new Double(-quality);
            ArrayList<Locale> list = locales.get(key);
            if (list == null) {
                list = new ArrayList<>();
                locales.put(key, list);
            }
            list.add(locale);
            count++;
        }
        Locale[] localeArr = new Locale[count];
        count = 0;
        //按权重加在数组中,权重越大越靠前
        for (ArrayList<Locale> list : locales.values()) {
            for (Locale locale : list) {
                localeArr[count++] = locale;
            }
        }
        return localeArr;
    }
    //给入要解析的字符串和字符编码,解析参数
    public static void parseParameters(Map<String, String[]> map, String data, String encoding) throws UnsupportedEncodingException {
        if (data == null || data.length() <= 0)
            return;
        parseParameters(map, data.getBytes(), encoding);

    }
    //给入要解析的字节数组和字符编码,解析参数
    public static void parseParameters(Map<String, String[]> map, byte[] data, String encoding) throws UnsupportedEncodingException {
        if (data == null || data.length <= 0)
            return;
        String key = null;
        String value = null;
        int index = 0;
        int strIndex = 0;
        while (index < data.length) {
            byte c = data[index++];
            switch ((char)c) {
                case '&':
                    //说明strIndex前面是value
                    value = new String(data, 0, strIndex, encoding);
                    if (key != null && key.length() > 0) {
                        putMapEntry(map, key, value);
                        key = null;
                    }
                    //重新开始记录下一个串
                    strIndex = 0;
                    break;
                case '=':
                    //说明strIndex前面是key
                    key = new String(data, 0, strIndex, encoding);
                    //重新开始记录下一个串
                    strIndex = 0;
                    break;
                case '+':
                    //+换成空格
                    //　W3C标准规定，当Content-Type为application/x-www-form-urlencoded时，URL中查询参数名和参数值中空格要用加号+替代，所以几乎所有使用该规范的浏览器在表单提交后，URL查询参数中空格都会被编成加号+。
                    // 而在另一份规范RFC2396，定义URI里, URI里的保留字符都需转义成%HH格式(Section 3.4 Query Component)，因此空格会被编码成%20，加号+本身也作为保留字而被编成%2B，对于某些遵循RFC 2396标准的应用来说，它可能不接受查询字符串中出现加号+，认为它是非法字符。
                    data[strIndex++] = ' ';
                    break;
                case '%':
                    byte high = convertHexDigit(data[index++]);
                    byte low = convertHexDigit(data[index++]);
                    data[strIndex++] = (byte)((high << 4) | low);
                    break;
                default:
                    data[strIndex++] = c;
            }

        }
        //最后一对参数
        if (key != null && key.length() > 0) {
            value = new String(data, 0, strIndex, encoding);
            putMapEntry(map, key, value);
        }

    }
    //16进制转10进制(0 <= b <= f)
    private static byte convertHexDigit(byte b) {
        if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
        if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
        if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
        return 0;
    }
    //往map中放数据
    private static void putMapEntry(Map<String, String[]> map, String name, String value) {
        String[] oldValues = map.get(name);
        String[] newValues = null;
        if (oldValues == null) {
            newValues = new String[1];
            newValues[0] = value;
        } else {
            newValues = new String[oldValues.length + 1];
            System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
            newValues[oldValues.length] = value;
        }
        map.put(name, newValues);
    }

    //获取解码后的uri(把%xx转为字符)
    public static String URLDecode(String str) {
        return URLDecode(str, null);
    }
    //获取解码后的uri(把%xx转为字符)
    public static String URLDecode(String str, String enc) {
        if (str == null)
            return null;
        byte[] bytes = str.getBytes();
        return URLDecode(bytes, enc);
    }
    //获取解码后的uri(把%xx转为字符)
    public static String URLDecode(byte[] bytes, String enc) {
        if (bytes == null)
            return null;

        int len = bytes.length;
        int ix = 0;
        int ox = 0;
        while (ix < len) {
            byte b = bytes[ix++];     // Get byte to test
            if (b == '+') {
                b = (byte)' ';
            } else if (b == '%') {
                b = (byte) ((convertHexDigit(bytes[ix++]) << 4)
                        + convertHexDigit(bytes[ix++]));
            }
            bytes[ox++] = b;
        }
        if (enc != null) {
            try {
                return new String(bytes, 0, ox, enc);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return new String(bytes, 0, ox);
    }

}
